package com.xqboss.apps.task;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjUtil;
import com.xqboss.apps.constant.Config;
import com.xqboss.apps.domain.hama.*;
import com.xqboss.apps.domain.sys.Job;
import com.xqboss.apps.enums.hama.HamaConfigNumberPeopleEnum;
import com.xqboss.apps.enums.hama.HamaDelayExpireLogTypeEnum;
import com.xqboss.apps.enums.hama.HamaMixLogHireEnum;
import com.xqboss.apps.enums.sys.JobConfigStateEnum;
import com.xqboss.apps.service.hama.*;
import com.xqboss.apps.service.sys.JobService;
import com.xqboss.common.exception.ServiceException;
import com.xqboss.common.utils.AsyncHandlerUtil;
import com.xqboss.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;


/**
 * 运动场空投、开关定时任务
 *
 */
@Slf4j
@Component("sportTask")
public class SportTask {

    @Autowired
    private HamaMixLogService hamaMixLogService;

    @Autowired
    private HamaUserMixService hamaUserMixService;

    @Autowired
    private HamaHelpService hamaHelpService;

    @Autowired
    private HamaUserOpenService hamaUserOpenService;

    @Autowired
    private ISysConfigService sysConfigService;

    @Autowired
    private JobService jobService;

    @Autowired
    private HamaDelayExpireLogService hamaDelayExpireLogService;


    /**
     * 运动场投放装备空投力豆
     *
     */
    @Transactional
    public void airdropSportMix(){
        if (!isOpen(HamaDelayExpireLogTypeEnum.MIX)) return;
        log.info("运动场投放装备空投力豆=====================>>>>>>>>>>>>>>>>开始啦>>>>>>>>>>>>");
        this.hamaUserMixService.airdropSportMix();
    }

    /**
     * 运动场队员助力空投力豆
     *
     */
    @Transactional
    public void airdropSportTeam(){
        if (!isOpen(HamaDelayExpireLogTypeEnum.TEAM)) return;
        log.info("运动场队员助力空投力豆=====================>>>>>>>>>>>>>>>>开始啦>>>>>>>>>>>>");
        this.hamaUserMixService.airdropSportTeam();
    }

    /**
     * 构建运动场空投力豆
     *
     */
    @Transactional
    public void airdropSportBuild() {
        if (!isOpen(HamaDelayExpireLogTypeEnum.BUILD)) return;
        log.info("构建运动场空投力豆=====================>>>>>>>>>>>>>>>>开始啦>>>>>>>>>>>>");
        this.hamaUserMixService.airdropSportBuild();
    }

    /**
     * 运动场开关
     *
     * @param status 0-关闭 1-开启
     *
     */
    public void sportSwitch(Integer status) {
        status = status == null ? 0 : status;
        Assert.isFalse(status!=0 && status!=1,()->new ServiceException("运动场开关状态（0-关闭 1-开启）！！！"));
        boolean update = sysConfigService.updateConfig(Config.Sys.Sport.运动场开关, status);
        sysConfigService.resetConfigCache();
        Assert.isTrue(update,()->new ServiceException("操作运动场周末开关失败！"));
        // 只要是开启，那么就重置生产时间为上一个整时
        if (status == 1) {
            // 若该时间段没有配置，则可空投，则设置生产时间为上一个整时，整小时,比如：2024-03-04 00:00:00,这个时间即为：运动场开始生产的时间
            LocalDateTime localDateTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS);// 当前整时
            LocalDateTime lastDateTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS).minusHours(1);// 上一个整时
            log.info("============>>>>>>>>>>>>开始重置用户的开始生产时间为上一个整时，当前整时：{}，上一个整时：{}",localDateTime,lastDateTime);
            // 重置所有用户：运动场投放装备的开始生产时间
            this.hamaUserMixService.lambdaUpdate().set(HamaUserMix::getStartProductionTime, lastDateTime).gt(HamaUserMix::getBasicSpeed,BigDecimal.ZERO).update();
            // 重置所有用户：队员助力的开始生产时间
            this.hamaHelpService.lambdaUpdate().set(HamaHelp::getStartProductionTime, lastDateTime).ne(HamaHelp::getIsPeople,HamaConfigNumberPeopleEnum.ADMIN).gt(HamaHelp::getBasicSpeed,BigDecimal.ZERO).eq(HamaHelp::getIsRealName,Boolean.TRUE).update();
            // 重置所有用户：建设的运动场的开始生产时间
            this.hamaUserOpenService.lambdaUpdate().set(HamaUserOpen::getStartProductionTime, lastDateTime).eq(HamaUserOpen::getIsHire, HamaMixLogHireEnum.YES).update();
        }
        log.info("运动场开关=====================>>>>>>>>>>>>>>>>{}啦>>>>>>>>>>>>",(status==1)?"开起":"关闭");
    }

    /** 0
     * TODO: 校验是否开启空投力豆
     *
     *       根据后台时间段配置确定，配置了时间段，且当前时间在该时间段，那么停止空投，延期空投过期时间 1小时
     *
     * @return true-开启空投
     */
    public boolean isOpen(HamaDelayExpireLogTypeEnum type) {
        LocalDateTime dateTime = LocalDateTime.now().truncatedTo(ChronoUnit.HOURS);//当前整时
        Job job = jobService.isAirdrop(JobConfigStateEnum.SPORT,dateTime);
        boolean isAirdrop = job==null;
        Integer isOpen = this.sysConfigService.getValue(Config.Sys.Sport.运动场开关);
        if (isAirdrop) {
            if (isOpen==0) {
                sportSwitch(1);
            }
            return true;
        }else{
            if (isOpen==1) {
                sportSwitch(0);
            }
            // 为false，表明不可空投，为所有用户中【投装备空投】和【新人助力空投】和【建设运动场】的过期时间延后一个小时
            delayExpiration(type,job,isOpen,dateTime,1);//空投过期延时
            return false;
        }
    }

    /**
     * 空投周期过期时间，延时多少小时
     *
     * @param job 定时任务
     * @param isOpen 0-关闭 1-开启
     * @param hour 多少小时
     */
    public void delayExpiration(HamaDelayExpireLogTypeEnum type,Job job,int isOpen,LocalDateTime now,int hour) {
        List<HamaDelayExpireLog> logs = null;
        HamaDelayExpireLog log;

        // (只在开始关闭空投时间之后，和要开启空投时间之前，执行一次)
        boolean isLogs = getIsLogs(job,now);

        //【mix】 投放装备空投过期延时
        if (type == HamaDelayExpireLogTypeEnum.MIX) {
            List<HamaMixLog> mixLogs = this.hamaMixLogService.lambdaQuery().select(HamaMixLog::getId,HamaMixLog::getUserId,HamaMixLog::getHireTime).eq(HamaMixLog::getIsHire, HamaMixLogHireEnum.YES).orderByDesc(HamaMixLog::getUserId).list();
            Set<Long> userIds = mixLogs.stream().map(HamaMixLog::getUserId).collect(Collectors.toSet());
            List<HamaUserMix> mixes = this.hamaUserMixService.lambdaQuery().select(HamaUserMix::getUserId,HamaUserMix::getStartProductionTime,HamaUserMix::getEggLayingNum,HamaUserMix::getBasicSpeed).in(HamaUserMix::getUserId, userIds).list();
            Map<Long, HamaUserMix> mixMap = mixes.stream().collect(Collectors.toMap(HamaUserMix::getUserId, Function.identity()));
            logs = new ArrayList<>(mixLogs.size());
            List<HamaMixLog> mixLogsUpdate = new ArrayList<>(mixLogs.size());
            HamaMixLog mixLogUpdate;
            for (HamaMixLog mixLog : mixLogs) {
                HamaUserMix mix = mixMap.get(mixLog.getUserId());
                boolean notEmpty = ObjUtil.isNotEmpty(mix);
                DateTime delayDate = DateUtil.offsetHour(mixLog.getHireTime(), hour);
                if (isLogs) {
                    log = HamaDelayExpireLog.builder()
                            .jobId(job.getId())
                            .isOpen(isOpen)
                            .delayHour(hour)
                            .userId(mixLog.getUserId())
                            .type(type) // 请根据实际的枚举类型进行替换
                            .sourceId(mixLog.getId())
                            .beforeDelayDate(mixLog.getHireTime())
                            .afterDelayDate(delayDate)
                            .startProductionTime(notEmpty?mix.getStartProductionTime():null)
                            .eggLayingNum(notEmpty?mix.getEggLayingNum():BigDecimal.ZERO)
                            .basicSpeed(notEmpty?mix.getBasicSpeed():BigDecimal.ZERO)
                            .build();
                    log.setCreateTime(now);
                    // 累加日志
                    logs.add(log);
                }
                // 延时过期时间
                mixLogUpdate = new HamaMixLog();
                mixLogUpdate.setId(mixLog.getId());
                mixLogUpdate.setHireTime(delayDate);
                mixLogUpdate.setUpdateTime(now);
                mixLogsUpdate.add(mixLogUpdate);
            }
            // 批量更新延期
            if(!mixLogsUpdate.isEmpty())this.hamaMixLogService.updateBatchById(mixLogsUpdate);
        }

        //【team】 新人助力空投过期延时
        if (type == HamaDelayExpireLogTypeEnum.TEAM) {
            List<HamaHelp> helps = this.hamaHelpService.lambdaQuery().select(HamaHelp::getId,HamaHelp::getChildUserId,HamaHelp::getHireTime,HamaHelp::getStartProductionTime,HamaHelp::getEggLayingNum,HamaHelp::getBasicSpeed).eq(HamaHelp::getIsPeople, HamaConfigNumberPeopleEnum.NEW).gt(HamaHelp::getBasicSpeed, 0).list();
            logs = new ArrayList<>(helps.size());
            List<HamaHelp> helpsUpdate = new ArrayList<>(helps.size());
            HamaHelp helpUpdate;
            for (HamaHelp help : helps) {
                DateTime delayDate = DateUtil.offsetHour(help.getHireTime(), hour);
                if (isLogs) {
                    log = HamaDelayExpireLog.builder()
                            .jobId(job.getId())
                            .isOpen(isOpen)
                            .delayHour(hour)
                            .userId(help.getChildUserId())
                            .type(type) // 请根据实际的枚举类型进行替换
                            .sourceId(help.getId())
                            .beforeDelayDate(help.getHireTime())
                            .afterDelayDate(delayDate)
                            .startProductionTime(help.getStartProductionTime())
                            .eggLayingNum(help.getEggLayingNum())
                            .basicSpeed(help.getBasicSpeed())
                            .build();
                    log.setCreateTime(now);
                    // 累加日志
                    logs.add(log);
                }
                // 延时过期时间
                helpUpdate = new HamaHelp();
                helpUpdate.setId(help.getId());
                helpUpdate.setHireTime(delayDate);
                helpUpdate.setUpdateTime(now);
                helpsUpdate.add(helpUpdate);
            }
            // 批量更新延期
            if(!helpsUpdate.isEmpty())this.hamaHelpService.updateBatchById(helpsUpdate);
        }

        //【build】 建设运动场空投过期延时
        if (type == HamaDelayExpireLogTypeEnum.BUILD) {
            List<HamaUserOpen> opens = this.hamaUserOpenService.lambdaQuery().select(HamaUserOpen::getId,HamaUserOpen::getUserId,HamaUserOpen::getHireTime,HamaUserOpen::getStartProductionTime,HamaUserOpen::getEggLayingNum,HamaUserOpen::getBasicSpeed).eq(HamaUserOpen::getIsHire, HamaMixLogHireEnum.YES).gt(HamaUserOpen::getBasicSpeed,0).orderByDesc(HamaUserOpen::getUserId).list();
            logs = new ArrayList<>(opens.size());
            List<HamaUserOpen> opensUpdate = new ArrayList<>(opens.size());
            HamaUserOpen openUpdate;
            for (HamaUserOpen open : opens) {
                DateTime delayDate = DateUtil.offsetHour(open.getHireTime(), hour);
                if (isLogs) {
                    log = HamaDelayExpireLog.builder()
                            .jobId(job.getId())
                            .isOpen(isOpen)
                            .delayHour(hour)
                            .userId(open.getUserId())
                            .type(type) // 请根据实际的枚举类型进行替换
                            .sourceId(open.getId())
                            .beforeDelayDate(open.getHireTime())
                            .afterDelayDate(delayDate)
                            .startProductionTime(open.getStartProductionTime())
                            .eggLayingNum(open.getEggLayingNum())
                            .basicSpeed(open.getBasicSpeed())
                            .build();
                    log.setCreateTime(now);
                    // 累加日志
                    logs.add(log);
                }
                // 延时过期时间
                openUpdate = new HamaUserOpen();
                openUpdate.setId(open.getId());
                openUpdate.setHireTime(delayDate);
                openUpdate.setUpdateTime(now);
                opensUpdate.add(openUpdate);
            }
            // 批量更新延期
            if(!opensUpdate.isEmpty())this.hamaUserOpenService.updateBatchById(opensUpdate);
        }


        // 批量保存日志(只在开始关闭空投时间之后，和要开启空投时间之前，执行一次)
        if(logs!=null) {
            List<HamaDelayExpireLog> finalLogs = logs;
            AsyncHandlerUtil.run(()->this.hamaDelayExpireLogService.saveLogs(finalLogs));
        }
    }

    /**
     *只在开始关闭空投时间之后，和要开启空投时间之前，执行一次
     *
     *
     * @param job 定时任务
     * @param now 当前时间
     * @return true-记录日志
     */
    private boolean getIsLogs(Job job, LocalDateTime now) {
        LocalDateTime jobCloseTime = LocalDateTimeUtil.of(job.getJobCloseTime());// 空投关闭时间
        LocalDateTime jobOpenTime = LocalDateTimeUtil.of(job.getJobOpenTime());// 空投开启时间
        long minutes = Duration.between(now, jobCloseTime).toMinutes();
        if (minutes >=0 && minutes < 60) {
            log.info("空投已经关闭~");
            return true;
        }
        minutes = Duration.between(now, jobOpenTime).toMinutes();
        if (minutes <= 60) {
            // 该时间段占停空投结束，设置当前定时任务未可删除
            this.jobService.lambdaUpdate().set(Job::getIsUse,false).eq(Job::getId,job.getId()).update();
            log.info("空投即将开启~");
            return true;
        }
        return false;
    }
}
